﻿using RockBus.CacheItemConfiguration;
using RockBus.CacheManager.Interfaces;
using RockBus.DataContracts;
using RockBus.DataContracts.ConfigurationService;
using RockBus.Logging.Interfaces;
using RockBus.ConfigurationRepository.Interfaces;
using System;

namespace RockBus.ConfigurationRepository.Cache
{
    public class ConfigurationRepository : IConfigurationRepository
    {
        public ConfigurationRepository(
            ILogger logger,
            ICacheManager cacheManager,
            ICacheItemConfiguration publisherCacheItemConfiguration,
            ICacheItemConfiguration subscriberCacheItemConfiguration)
        {
            this.Logger = logger;
            this.CacheManager = cacheManager;
            this.PublisherCacheItemConfiguration = publisherCacheItemConfiguration;
            this.SubscriberCacheItemConfiguration = subscriberCacheItemConfiguration;
        }

        private ILogger Logger { get; set; }

        private ICacheManager CacheManager { get; set; }

        public ICacheItemConfiguration SubscriberCacheItemConfiguration { get; set; }

        public ICacheItemConfiguration PublisherCacheItemConfiguration { get; set; }

        private readonly object _lockObject = new object();

        public void AddOrUpdateSubscriberConfiguration(SubscriberConfiguration subscriberConfiguration)
        {
            lock (_lockObject)
            {
                SubscriberConfiguration scExisting = this.CacheManager.Get<SubscriberConfiguration>(this.SubscriberCacheItemConfiguration.CacheItemName);
                if (null == scExisting)
                {
                    this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, subscriberConfiguration);
                    return;
                }

                subscriberConfiguration.SetModificationDateTime(DateTime.UtcNow);
                scExisting.Merge(subscriberConfiguration);
                if (this.SubscriberCacheItemConfiguration.TimeToLiveSecs <= 0)
                {
                    this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, scExisting);
                }
                else
                {
                    this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, scExisting, this.SubscriberCacheItemConfiguration.TimeToLiveSecs);
                }
            }
        }

        public void RemoveSubscribers(RemoveSubscribersRequest request)
        {
            lock (_lockObject)
            {
                SubscriberConfiguration scExisting = this.CacheManager.Get<SubscriberConfiguration>(this.SubscriberCacheItemConfiguration.CacheItemName);
                if (null != scExisting)
                {
                    foreach (string subscriberId in request.SubscriberIds)
                    {
                        if (!scExisting.EvaluationElementDictionary.Remove(subscriberId))
                        {
                            return;
                        }
                    }

                    if (this.SubscriberCacheItemConfiguration.TimeToLiveSecs <= 0)
                    {
                        this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, scExisting);
                    }
                    else
                    {
                        this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, scExisting, this.SubscriberCacheItemConfiguration.TimeToLiveSecs);
                    }
                }
            }
        }

        public void RemoveSubscriptions(RemoveSubscriptionsRequest request)
        {
            lock (_lockObject)
            {
                SubscriberConfiguration scExisting = this.CacheManager.Get<SubscriberConfiguration>(this.SubscriberCacheItemConfiguration.CacheItemName);
                if (null != scExisting)
                {
                    SubscriberInfo si = null;
                    if (!scExisting.EvaluationElementDictionary.TryGetValue(request.SubscriberId, out si))
                    {
                        return;
                    }

                    bool somethingChanged = false;
                    foreach (string subscriptionId in request.SubscriptionIds)
                    {
                        if (si.InfoDictionary.Remove(subscriptionId))
                        {
                            somethingChanged = true;
                        }
                    }

                    if (!somethingChanged)
                    {
                        return;
                    }

                    if (this.SubscriberCacheItemConfiguration.TimeToLiveSecs <= 0)
                    {
                        this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, scExisting);
                    }
                    else
                    {
                        this.CacheManager.Set(this.SubscriberCacheItemConfiguration.CacheItemName, scExisting, this.SubscriberCacheItemConfiguration.TimeToLiveSecs);
                    }
                }
            }
        }

        public SubscriberConfiguration GetSubscriberConfiguration()
        {
            SubscriberConfiguration scExisting = this.CacheManager.Get<SubscriberConfiguration>(this.SubscriberCacheItemConfiguration.CacheItemName);
            if (null == scExisting)
            {
                return new SubscriberConfiguration();
            }

            scExisting.PurgeExpired(DateTime.UtcNow);
            return scExisting;
        }

        public void AddOrUpdatePublisherConfiguration(PublisherConfiguration publisherConfiguration)
        {
            lock (_lockObject)
            {
                PublisherConfiguration pcExisting = this.CacheManager.Get<PublisherConfiguration>(this.PublisherCacheItemConfiguration.CacheItemName);
                if (null == pcExisting)
                {
                    this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, publisherConfiguration);
                    return;
                }

                //request.PublisherConfiguration.SetModificationDateTime(DateTime.UtcNow);
                pcExisting.Merge(publisherConfiguration);
                if (this.PublisherCacheItemConfiguration.TimeToLiveSecs <= 0)
                {
                    this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, pcExisting);
                }
                else
                {
                    this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, pcExisting, this.PublisherCacheItemConfiguration.TimeToLiveSecs);
                }
            }
        }

        public void RemovePublishers(RemovePublishersRequest request)
        {
            lock (_lockObject)
            {
                PublisherConfiguration scExisting = this.CacheManager.Get<PublisherConfiguration>(this.PublisherCacheItemConfiguration.CacheItemName);
                if (null != scExisting)
                {
                    foreach (string subscriberId in request.PublisherIds)
                    {
                        if (!scExisting.EvaluationElementDictionary.Remove(subscriberId))
                        {
                            return;
                        }
                    }

                    if (this.PublisherCacheItemConfiguration.TimeToLiveSecs <= 0)
                    {
                        this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, scExisting);
                    }
                    else
                    {
                        this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, scExisting, this.PublisherCacheItemConfiguration.TimeToLiveSecs);
                    }
                }
            }
        }

        public void RemoveReceivePorts(RemoveReceivePortsRequest request)
        {
            lock (_lockObject)
            {
                PublisherConfiguration scExisting = this.CacheManager.Get<PublisherConfiguration>(this.PublisherCacheItemConfiguration.CacheItemName);
                if (null != scExisting)
                {
                    PublisherInfo si = null;
                    if (!scExisting.EvaluationElementDictionary.TryGetValue(request.PublisherId, out si))
                    {
                        return;
                    }

                    bool somethingChanged = false;
                    foreach (string subscriptionId in request.ReceivePortIds)
                    {
                        if (si.InfoDictionary.Remove(subscriptionId))
                        {
                            somethingChanged = true;
                        }
                    }

                    if (!somethingChanged)
                    {
                        return;
                    }

                    if (this.PublisherCacheItemConfiguration.TimeToLiveSecs <= 0)
                    {
                        this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, scExisting);
                    }
                    else
                    {
                        this.CacheManager.Set(this.PublisherCacheItemConfiguration.CacheItemName, scExisting, this.PublisherCacheItemConfiguration.TimeToLiveSecs);
                    }
                }
            }
        }

        public PublisherConfiguration GetPublisherConfiguration()
        {
            PublisherConfiguration scExisting = this.CacheManager.Get<PublisherConfiguration>(this.PublisherCacheItemConfiguration.CacheItemName);
            if (null == scExisting)
            {
                return new PublisherConfiguration();
            }

            scExisting.PurgeExpired(DateTime.UtcNow);
            return scExisting;
        }

        public void RemoveAllSubscribers(RemoveAllSubscribersRequest request)
        {
            this.CacheManager.Remove<SubscriberConfiguration>(this.SubscriberCacheItemConfiguration.CacheItemName);
        }

        public void RemoveAllPublishers(RemoveAllPublishersRequest request)
        {
            this.CacheManager.Remove<PublisherConfiguration>(this.PublisherCacheItemConfiguration.CacheItemName);
        }

        public SubscriberInfo GetSubscriber(GetSubscriberRequest request)
        {
            // TODO: Implement this
            throw new NotImplementedException();
        }

        private static string InternalConfigurationCacheName = "InternalConfiguration";
        public InternalConfiguration GetInternalConfiguration()
        {
            var configuration = this.CacheManager.Get<InternalConfiguration>(InternalConfigurationCacheName);
            return configuration;
        }

        public void SetInternalConfiguration(InternalConfiguration configuration)
        {
            this.CacheManager.Set<InternalConfiguration>(InternalConfigurationCacheName, configuration);
        }

        public void ClearAllClients()
        {
            this.RemoveAllSubscribers(new RemoveAllSubscribersRequest());
            this.RemoveAllPublishers(new RemoveAllPublishersRequest());
        }
    }
}